/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.entity.propertyinfobuilder.cef;

import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.common.BefDtBeanUtil;
import com.inspur.edp.bef.engine.entity.exception.BefEngineExceptionBase;
import com.inspur.edp.bef.engine.entity.exception.ErrorCodes;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.ForeignKeyConstraintType;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.designtime.api.element.GspDeleteRuleType;
import com.inspur.edp.cef.spi.entity.AssociationEnableState;
import com.inspur.edp.cef.spi.entity.AssociationInfo;
import com.inspur.edp.cef.spi.entity.DeleteCheckState;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.AssocationPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.BasePropertyInfo;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.entity.GspCommonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

public class AssoPropInfoBuilder extends BasePropertyInfoBuilder {

    static Logger logger = LoggerFactory.getLogger(AssoPropInfoBuilder.class);

    public AssoPropInfoBuilder(IGspCommonField field,
                               CefPropInfoGenerator propInfoGenerator) {
        super(field, propInfoGenerator);
    }


    private GspBusinessEntity getRefEntity(String id) {
        return CMEngineUtil.getMetadataContent(id);
    }

    protected GspAssociation getAssociation() {
        return field.getChildAssociations().get(0);
    }

    @Override
    public BasePropertyInfo getBasePropertyInfo(IGspCommonField field) {
        GspAssociation asso = getAssociation();
        if (asso == null || StringUtils.isEmpty(asso.getRefModelID())) {
            throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0003, field.getBelongObject().getCode(),
                    field.getLabelID());

        }
        GspBusinessEntity refEntity = getRefEntity(asso.getRefModelID());
        if (refEntity == null) {
            String beId = "";
            if(field.getBelongObject() instanceof GspCommonObject){
                GspCommonObject gspCommonObject = (GspCommonObject) field.getBelongObject();
                beId = gspCommonObject.getBelongModel().getID();
            }

            throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0004, field.getBelongObject().getCode(),
                    field.getLabelID(), asso.getRefModelID(), asso.getRefModelName(), beId);

        }
        return getAssoPropertyInfo(field, getAssociation(), refEntity, propInfoGenerator);
    }

    public static AssocationPropertyInfo getAssoPropertyInfo(IGspCommonField field, GspAssociation association, GspBusinessEntity refEntity, CefPropInfoGenerator propInfoGenerator) {
        AssociationInfo associationInfo = new AssociationInfo();
        associationInfo.setConfig(refEntity.getGeneratedConfigID());
        associationInfo
                .setNodeCode(getObjectByAssociation(association, refEntity).getCode());
        associationInfo.setMainCode(refEntity.getMainObject().getCode());
        setEnableState(associationInfo, association);
        setDeleteCheckState(associationInfo, association);
        setAssoKeys(associationInfo, field, association, refEntity);
        setRefProperties(associationInfo, association, propInfoGenerator,
                getObjectByAssociation(association, refEntity), refEntity);
        associationInfo.setKeepAssoPropertyForExpression(association.isKeepAssoPropertyForExpression());

        EngineAssoPropertyInfo assocationPropertyInfo = new EngineAssoPropertyInfo(associationInfo, association);
        return assocationPropertyInfo;
    }

    private static IGspCommonObject getObjectByAssociation(GspAssociation association,
                                                           GspBusinessEntity refEntity) {
        IGspCommonObject result = null;
        if (!StringUtils.isEmpty(association.getRefObjectID())) {
            result = refEntity.findObjectById(association.getRefObjectID());
        }
        if (result == null && !StringUtils.isEmpty(association.getRefObjectCode())) {
            result = refEntity.findObjectByCode(association.getRefObjectCode());
        }
        if (result == null) {
            throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0005, refEntity.getCode(),
                    association.getRefModelID(), association.getRefModelCode());

        }
        return result;
    }

    private static void setRefProperties(AssociationInfo associationInfo, GspAssociation association, CefPropInfoGenerator propInfoGenerator, IGspCommonObject objectById, GspBusinessEntity refEntity) {
        for (IGspCommonField refField : association.getRefElementCollection()) {
            IGspCommonField refedField = objectById.findElement(refField.getRefElementId());
            if (refedField == null){
                IGspCommonField curElement = association.getBelongElement();
                String beId = ((IGspCommonObject)curElement.getBelongObject()).getBelongModelID();
                throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0006, association.getBelongElement().getName(), refField.getName(), refEntity.getName(),
                        beId, curElement.getBelongObject().getID(), curElement.getID(), refField.getRefElementId(), objectById.getBelongModelID());
            }

            associationInfo.addRefProp(refField.getLabelID(), propInfoGenerator.getAssociationRefPropertyInfo(refField, refedField));
        }
    }

    private static void setAssoKeys(AssociationInfo associationInfo, IGspCommonField field, GspAssociation association, GspBusinessEntity refEntity) {
        setPrivateSourceColumn(associationInfo, association, refEntity);
        setPrivateTargetColumn(associationInfo, field);
    }

    private static void setPrivateTargetColumn(AssociationInfo associationInfo, IGspCommonField field) {
        if (field.getIsRefElement()) {
            GspBusinessEntity entity = CMEngineUtil.getMetadataContent(field.getParentAssociation().getRefModelID());
            associationInfo.setPrivateTargetColumn(entity.findElementById(field.getRefElementId()).getLabelID());

        } else
            associationInfo.setPrivateTargetColumn(field.getLabelID());
    }

    private static void setPrivateSourceColumn(AssociationInfo associationInfo, GspAssociation association, GspBusinessEntity refEntity) {
        String sourceElementId = "";
        for (GspAssociationKey key : association.getKeyCollection()) {
            sourceElementId = key.getSourceElement();
        }
        IGspCommonObject commonObject = getObjectByAssociation(association, refEntity);
        if (commonObject == null) {
            throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0005, refEntity.getCode(),
                    association.getRefModelID(), association.getRefModelCode());
        }
        IGspCommonField sourceElement = commonObject.findElement(sourceElementId);

        if (sourceElement == null) {
            IGspCommonDataType currentObj = association.getBelongElement().getBelongObject();
            logger.error("当前实体ID为："+currentObj.getID()+"关联字段ID为："+association.getBelongElement().getID()+"被关联的元数据ID为："+association.getRefModelID()+"关联的实体ID为："+commonObject.getID());
            throw new BefEngineExceptionBase(ErrorCodes.BEF_ENGINE_0012, currentObj.getCode(), sourceElementId);
        }
        associationInfo.setPrivateSourceColumn(sourceElement.getLabelID());
    }

    /**
     * 设置是否启用外键检查
     * @param associationInfo
     * @param association
     */
    private static void setEnableState(AssociationInfo associationInfo, GspAssociation association) {
        associationInfo.setEnableState(association.getForeignKeyConstraintType() == ForeignKeyConstraintType.Forbid
                ? AssociationEnableState.Disabled
                : AssociationEnableState.Enabled);
    }

    /**
     * 设置是否启用删除检查
     * @param associationInfo
     * @param association
     */
    private static void setDeleteCheckState(AssociationInfo associationInfo, GspAssociation association) {
        associationInfo.setDeleteCheckState(association.getDeleteRuleType() == GspDeleteRuleType.Refuse
                ? DeleteCheckState.Enabled
                : DeleteCheckState.Disabled);
    }
}
